#include <rtthread.h>
#include <rtdevice.h>
#include "qx_hw_timer.h"
#include "hwtimer_config.h"
#include "cpu_port.h"

const rt_uint32_t hw_osc_freq_list[5] = {
    10000000,
     5000000,
     2500000,
     1250000,
      625000,
};

const rt_uint32_t hw_xtal_freq_list[5] = {
    16000000,
     8000000,
     4000000,
     2000000,
     1000000,
};

rt_uint32_t hw_support_freq[3*5];

void qx_build_timer_support_freq_list(void)
{
    for(rt_uint32_t i = 0; i < 5; i++){
        hw_support_freq[i] = SystemCoreClock/(1<<i);
    }

    rt_memcpy(&hw_support_freq[5],hw_osc_freq_list,sizeof(hw_osc_freq_list));

    rt_memcpy(&hw_support_freq[10],hw_xtal_freq_list,sizeof(hw_xtal_freq_list));
}


static CPUTimer_ClockSource timer_clk;
static rt_uint32_t clk_prescaler;

void qx_hw_prescaler_config_convert(rt_uint8_t index)
{
    clk_prescaler = index%5;
}

rt_err_t qx_hw_freq_config_convert(rt_uint32_t freq)
{
    rt_uint8_t index = 0;
    /* find support freq index */
    for(; index< sizeof(hw_support_freq);index++){
        if(hw_support_freq[index] == freq){
            break;
        }
    }

    if((index == sizeof(hw_support_freq) - 1) && (hw_support_freq[index] != freq)){
        return RT_ERROR;
    }

    if(index < 5){
        timer_clk = CPUTIMER_CLOCK_SOURCE_SYS;
        qx_hw_prescaler_config_convert(index);
    }else if(index < 10){
        timer_clk = CPUTIMER_CLOCK_SOURCE_INTOSC1;
        qx_hw_prescaler_config_convert(index - 5);
    }else{
        timer_clk = CPUTIMER_CLOCK_SOURCE_XTAL;
        qx_hw_prescaler_config_convert(index - 10);
    }

    return RT_EOK;
}

rt_err_t qx_calc_timer_presacler_value(rt_uint32_t freq)
{
    if(freq%2){
        return RT_ERROR;
    }

    clk_prescaler = (SystemCoreClock/freq)/2;

    if(clk_prescaler > 0xffffUL){
        return RT_ERROR;
    }

    return RT_EOK;
}

void qx_timer_init(struct rt_hwtimer_device *timer, rt_uint32_t state)
{
    struct qx_hwtimer* instance = RT_NULL;
    instance = rt_container_of(timer, struct qx_hwtimer,time_device);
    rt_uint32_t reg_addr = instance->timer_regbase;

    rt_err_t rst = qx_calc_timer_presacler_value(timer->freq);

    if(rst != RT_EOK){
        return;
    }

    if(state){
        CPUTimer_stopTimer(reg_addr);

        CPUTimer_setPreScaler(reg_addr,clk_prescaler);

        Interrupt_enable(instance->intr_type);

        CPUTimer_enableInterrupt(reg_addr);
    }else{

        CPUTimer_stopTimer(reg_addr);

        CPUTimer_disableInterrupt(reg_addr);
    }
}

rt_err_t qx_timer_start(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode)
{
    struct qx_hwtimer* instance = RT_NULL;
    instance = rt_container_of(timer, struct qx_hwtimer,time_device);

    CPUTimer_setPeriod(instance->timer_regbase,cnt);

    CPUTimer_startTimer(instance->timer_regbase);

    return RT_EOK;
}

void qx_timer_stop(struct rt_hwtimer_device *timer)
{
    struct qx_hwtimer* instance = RT_NULL;
    instance = rt_container_of(timer, struct qx_hwtimer,time_device);

    CPUTimer_stopTimer(instance->timer_regbase);
}

rt_uint32_t qx_timer_counter_get(struct rt_hwtimer_device *timer)
{
    struct qx_hwtimer* instance = RT_NULL;
    instance = rt_container_of(timer, struct qx_hwtimer,time_device);

    return CPUTimer_getTimerCount(instance->timer_regbase);
}

rt_err_t qx_timer_ctrl(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args)
{
    struct qx_hwtimer* instance = RT_NULL;
    instance = rt_container_of(timer, struct qx_hwtimer,time_device);

    rt_uint32_t reg_addr = instance->timer_regbase;

    rt_err_t rst = RT_EOK;

    switch(cmd){
        case HWTIMER_CTRL_FREQ_SET:{
            rt_uint32_t freq = *((rt_uint32_t *)args);
            rst = qx_calc_timer_presacler_value(freq);

            if(rst != RT_EOK){
                return rst;
            }

            CPUTimer_disableInterrupt(reg_addr);
            CPUTimer_stopTimer(reg_addr);
            CPUTimer_setPreScaler(reg_addr,clk_prescaler);
            CPUTimer_enableInterrupt(reg_addr);
            break;
        }
        default : break;
    }

    return rst;
}

#ifndef HW_TIMER_LIMIT_DEFINE
static const struct rt_hwtimer_info _info = HW_TIMER_LIMIT_DEFINE_DFT;
#else
static const struct rt_hwtimer_info _info = HW_TIMER_LIMIT_DEFINE;
#endif

static const struct rt_hwtimer_ops _ops =
{
    .init = qx_timer_init,
    .start = qx_timer_start,
    .stop = qx_timer_stop,
    .count_get = qx_timer_counter_get,
    .control = qx_timer_ctrl,
};

enum{
#if defined (BOARD_CFG_TIMER0)
        TIMER0_INDEX,
#endif

#if defined (BOARD_CFG_TIMER1)
        TIMER1_INDEX,
#endif

/* Timer2 has been used as system os tick */
};

static struct qx_hwtimer qx_hwtimer_obj[] =
{

#if defined(BOARD_CFG_TIMER0)
        TIMER0_CFG,
#endif

#if defined(BOARD_CFG_TIMER1)
        TIMER1_CFG,
#endif

/* Timer2 has been used as system os tick */
};

#ifdef BOARD_CFG_TIMER0
__interrupt void timer0_isr(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_device_hwtimer_isr(&(qx_hwtimer_obj[TIMER0_INDEX].time_device));

    /* leave interrupt */
    rt_interrupt_leave();

    CPUTimer_clearInterrupt(qx_hwtimer_obj[TIMER0_INDEX].timer_regbase);

}
#endif

#ifdef BOARD_CFG_TIMER1
__interrupt void timer1_isr(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_device_hwtimer_isr(&(qx_hwtimer_obj[TIMER1_INDEX].time_device));

    /* leave interrupt */
    rt_interrupt_leave();

    CPUTimer_clearInterrupt(qx_hwtimer_obj[TIMER1_INDEX].timer_regbase);
}
#endif

rt_err_t qx_hwtimer_init(void)
{
    rt_uint32_t i = 0;
    rt_err_t result = RT_EOK;

    for (i = 0; i < sizeof(qx_hwtimer_obj) / sizeof(qx_hwtimer_obj[0]); i++)
    {
        qx_hwtimer_obj[i].time_device.info = &_info;
        qx_hwtimer_obj[i].time_device.ops  = &_ops;
        if (rt_device_hwtimer_register(&qx_hwtimer_obj[i].time_device,
            qx_hwtimer_obj[i].name, RT_NULL) != RT_EOK)
        {
            result = -RT_ERROR;
        }
    }

    return result;
}

rt_err_t rt_hw_timer_init(void)
{
    return qx_hwtimer_init();
}

